use std::path::{Path, PathBuf};
use std::fs::{create_dir_all};
use crate::kvdb::traits::{DBError};


/*
pub trait DB {
    fn open(&mut self) -> Result<bool, DBError>;
    fn close(&mut self) -> Result<bool, DBError>;
    fn name(&self) -> &str;
    fn tables(&self) -> Ref<dyn Tables> ;
    fn tables_mut(&mut self) -> RefMut<dyn Tables>;
}*/
struct VersionSet {
    next_file_id : usize,
}

impl Default for VersionSet {
    fn default() -> Self{
        Self {
            next_file_id: 0,
        }
    }
}
impl VersionSet {
    fn next_file_id(&mut self) -> usize {
        self.next_file_id += 1;
        self.next_file_id
    }
}

struct Version {
}
impl Default for Version {
    fn default() -> Self {
        Version{
        }
    }
}

struct FileDB {
    dir: String,
    name: String,
    path: PathBuf,

    vset: VersionSet,
    base: Version,

    manifest_writer: ManifestWriter
}

impl FileDB {
    fn new(dir: &str, name: &str) -> Self{
        Self {
            dir: dir.to_string(),
            name: name.to_string(),
            path: path,//db entry path

            vset: VersionSet::default(), // version set 
            base: Version::default(), // base version

            manifest_writer: ManifestWriter::new()
        }
    }
    fn open(&mut self) -> Result<(), DBError> {
        if self.exist() {
            self._open()
        }
        else{
            self.create()
        }
    }

}






use std::fs::OpenOptions;
use std::io::Write;
fn save_current_file(path: &Path, file_name: &str)->std::io::Result<()>{
    let mut path_buf = PathBuf::from(path);
    path_buf.push("CURRENT");

    let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(path_buf.as_path()).unwrap();
    file.write(file_name.as_bytes())?;
    Ok(())
}



struct ManifestWriter {
    id: usize,
    path: PathBuf,
    writer: Option<Box<dyn Write>>,
}


impl ManifestWriter {
    fn manifest_file_name(id: usize) -> String {
        let mut s = String::from("MANIFEST-");
        s.push_str(&id.to_string());
        s
    }

    fn new() -> Self {
        Self {
            id : 0,
            path: PathBuf::new(),
            writer: None,
        }
    }



    fn open(&mut self, id: usize, dir: &Path) {
        self.path.push(Clone::cloned(dir));
        self.path.push(ManifestWriter::manifest_file_name(id))

        writer = Box::new(OpenOptions::new().write(true).create(true).append(true).open(self.apth.as_path()).unwrap());
    }

    fn write(&mut self, version: &Version) -> std::io::Result<usize>{
        writer.write(b"hello world;")
    }

}



impl FileDB {
    fn exist(&self) -> bool{
        self.path.as_path().is_dir()
    }

    fn path(&self) -> &Path {
        self.path.as_path()
    }

    fn create(&mut self) -> Result<(), DBError>{
        create_dir_all(self.path.as_path()).or(Err(DBError::FsError))?;

        //TODO: 
        self.manifest_writer.open(self.vset.nex_file_id(), self.path());
        self.manifest_writer.write(&self.base);

        save_current_file(self.path(), &manifest_file_name).or(Err(DBError::IoError))?;
        Ok(()) 
    }
    fn _open(&mut self) -> Result<(), DBError>{
        //TODO:  recover 
        Ok(())
    }
}





#[cfg(test)]
mod test {
    use super::*;
    //use std::fs::remove_dir_all;
    #[test]
    fn test(){
        let dir = "./meta";
        //println!("absolute: {}", absolute(".".to_string()).unwrap().as_path().display());
        std::fs::remove_dir_all(dir);
        
        let mut db = FileDB::new(".", "meta");
        assert!(!db.exist());

        assert!(db.open().is_ok());
        //TODO: assert!(db.close().is_ok());
        assert!(db.exist());
    }
}

